﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace GrainGrowth
{
    public partial class Form1 : Form
    {
        int sizeArea = 40;
        uint grainCount = 2;
        float width;
        Graphics g;
        Pen pen;
        int[,] field;
        int[,] fieldTMP;
        List<Color> colorLst;
        uint[] neighbour;
        Random rand;

        public Form1()
        {
            InitializeComponent();

            timer1.Interval = 300;

            PiB.Image = new Bitmap(480, 480);
            g = Graphics.FromImage(PiB.Image);
            pen = new Pen(Color.Black);
            rand = new Random();

            width = (float)PiB.Width / (float)sizeArea;
            field = new int[sizeArea, sizeArea];
            fieldTMP = new int[sizeArea, sizeArea];
            colorLst = new List<Color>();

            neighbour = new uint[2];

            numRand.Maximum = sizeArea * sizeArea;

            setArea();
        }

        private void setArea()
        {
            for (uint i = 0; i < sizeArea; i++)
            {
                for (uint j = 0; j < sizeArea; j++)
                {
                    field[i, j] = 0;
                    fieldTMP[i, j] = 0;
                    g.FillRectangle(new SolidBrush(Color.Orange), i * width, j * width, width, width);
                }
            }

            for (uint i = 0; i < grainCount; i++) neighbour[i] = 0;

            //random grain
            if (rdbRandom.Checked) randomGrain();


            for (uint i = 0; i < sizeArea; i++)
            {
                for (uint j = 0; j < sizeArea; j++)
                {
                    if (field[i, j] != 0) g.FillRectangle(new SolidBrush(colorLst[field[i, j] - 1]), i * width, j * width, width, width);
                }
            }

            PiB.Refresh();
        }

        private void randomGrain()
        {
            grainCount = (uint)numRand.Value;
            neighbour=new uint [grainCount];

            colorLst.Clear();
            for (uint i = 0; i < grainCount; i++)
            {
                field[rand.Next(1, sizeArea - 1), rand.Next(1, sizeArea - 1)] = (int)i + 1;
                colorLst.Add(Color.FromArgb(rand.Next(255), rand.Next(255), rand.Next(255)));
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            for (uint i = 0; i < sizeArea; i++)
            {
                for (uint j = 0; j < sizeArea; j++)
                {
                    GrowthMoore(i, j);

                    if (fieldTMP[i, j] != 0) g.FillRectangle(new SolidBrush(colorLst[fieldTMP[i, j] - 1]), i * width, j * width, width, width);
                }
            }

            AssignField();
            PiB.Refresh();
        }

        private void GrowthMoore(uint i, uint j)
        {
            if (field[i, j] == 0)
            {
                countNeighbour(i, j);

                int max = maxNeighbour();
                if(max != -1)   fieldTMP[i, j] = max + 1;
            }

            for (uint k = 0; k < grainCount; k++) neighbour[k] = 0;
        }

        private int maxNeighbour()
        {
            List<int> max = new List<int>();
            max.Add(0);     //contain index
            int m = (int)neighbour[0];

            for (uint i = 1; i < neighbour.Length; i++)
            {
                if (neighbour[max[max.Count - 1]] < neighbour[i])
                {
                    m = (int)neighbour[i];
                    max.Clear();
                    max.Add((int)i);
                }
                else if (neighbour[max[max.Count - 1]] == neighbour[i])
                {
                    max.Add((int)i);
                    neighbour[i] = 0;
                }
                ////////////////////////TUTAJ WYZEROWAć NEIGHBOURa neighbour[0] = 0
            }
            if (m == 0) return -1;
            else return max[rand.Next(max.Count - 1)];
        }

        private void countNeighbour(uint i, uint j)
        {
            for (uint k = 0; k < neighbour.Length; k++)
            {
                if (retLessIndex(i) != -1)
                    if (field[retLessIndex(i), j] == k + 1) neighbour[k]++;
                if (retLessIndex(i) != -1 && retLessIndex(j) != -1)
                    if (field[retLessIndex(i), retLessIndex(j)] == k + 1) neighbour[k]++;
                if (retLessIndex(i) != -1 && retGreaterIndex(j) != -1)
                    if (field[retLessIndex(i), retGreaterIndex(j)] == k + 1) neighbour[k]++;
                if (retGreaterIndex(i) != -1)
                    if (field[retGreaterIndex(i), j] == k + 1) neighbour[k]++;
                if (retGreaterIndex(i) != -1 && retLessIndex(j) != -1)
                    if (field[retGreaterIndex(i), retLessIndex(j)] == k + 1) neighbour[k]++;
                if (retGreaterIndex(i) != -1 && retGreaterIndex(j) != -1)
                    if (field[retGreaterIndex(i), retGreaterIndex(j)] == k + 1) neighbour[k]++;
                if (retGreaterIndex(j) != -1)
                    if (field[i, retGreaterIndex(j)] == k + 1) neighbour[k]++;
                if (retLessIndex(j) != -1)
                    if (field[i, retLessIndex(j)] == k + 1) neighbour[k]++;
            }
        }

        private int retLessIndex(uint x)
        {
            if (checkPeriodic.Checked)
            {
                if (((int)x - 1) < 0) return sizeArea - 1;
                else return (int)x - 1;
            }
            else
            {
                if (((int)x - 1) < 0) return -1;
                else return (int)x - 1;
            }
        }

        private int retGreaterIndex(uint x)
        {
            if (checkPeriodic.Checked)
            {
                if (((int)x + 1) > sizeArea - 1) return 0;
                else return ((int)x + 1) % sizeArea;
            }
            else
            {
                if (((int)x + 1) > sizeArea - 1) return -1;
                else return (int)x + 1;
            }
        }

        private void AssignField()
        {
            for (uint i = 0; i < sizeArea; i++)
            {
                for (uint j = 0; j < sizeArea; j++)
                {
                    field[i, j] = fieldTMP[i, j];
                }
            }
        }

        private void start_Click(object sender, EventArgs e)
        {
            //if (rdbRandom.Checked) randomGrain();
            hScrollBar1.Enabled = false;
            timer1.Start();
        }

        private void stop_Click(object sender, EventArgs e)
        {
            hScrollBar1.Enabled = true;
            timer1.Stop();
        }

        private void reset_Click(object sender, EventArgs e)
        {
            hScrollBar1.Enabled = true;
            timer1.Stop();

            colorLst.Clear();

            setArea();
        }

        private void hScrollBar1_Scroll(object sender, ScrollEventArgs e)
        {
            sizeArea = hScrollBar1.Value;
            width = (float)PiB.Width / (float)sizeArea;
            field = new int[sizeArea, sizeArea];
            fieldTMP = new int[sizeArea, sizeArea];
            setArea();
        }

        private void PiB_MouseDown(object sender, MouseEventArgs e)
        {
            if (rdbManual.Checked)
            {
                int posX = (int)((e.X - (e.X % width)) / width);
                int posY = (int)((e.Y - (e.Y % width)) / width);

                field[posX, posY] = colorLst.Count + 1;
                colorLst.Add(Color.FromArgb(rand.Next(255), rand.Next(255), rand.Next(255)));

                g.FillRectangle(new SolidBrush(colorLst[colorLst.Count - 1]), posX * width, posY * width, width, width);

                PiB.Refresh();
            }
        }
    }
}
